/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package com.csm.sim2k11.entities;

import com.csm.sim2k11.simulator.Configuration;
import com.csm.sim2k11.simulator.Simulator;
import csm.uttils.Generator;

/**
 *
 * @author santi
 */
public class Table {

    public final static int FREE = 0;
    public final static int TAKING_ORDER = 1;
    public final static int COOKING = 2;
    public final static int DELIVERING = 3;
    public final static int EATING = 4;
    private int state = FREE;
    private int tableID;
    private Client client;
    private String[] menu, menuRnds;
    private double nextFinish, nextEndLunch;
    private String rndLunch;

    public Table(int tableID) {
        this.tableID = tableID;
        state = FREE;
        menu = new String[]{"--", "--", "--", "--"};
        menuRnds = new String[]{"--", "--", "--", "--"};
        rndLunch = "--";
        nextEndLunch = nextFinish = -1;
    }

    /**
     * @return the state
     */
    public int getState() {
        return state;
    }

    public String getPrittyState() {
        switch (state) {
            case FREE:
                return "Libre";
            case TAKING_ORDER:
                return "Tomando la Orden C" + client.getID();
            case COOKING:
                return "Cocinando Orden C" + client.getID();
            case DELIVERING:
                return "Entregando Orden C" + client.getID();
            case EATING:
                return "Consumiendo Orden C" + client.getID();
            default:
                return "ESTADO NO RECONOCIDO";
        }
    }

    public int toNextState(double actualTime)
    {
        switch (state) {
            case FREE:
                updateState(TAKING_ORDER, actualTime);
                return -1;
            case TAKING_ORDER:
                updateState(COOKING, actualTime);
                return Simulator.FINISH_TAKING_ORDER;
            case COOKING:
                updateState(DELIVERING, actualTime);
                return Simulator.FINISH_COOKING;
            case DELIVERING:
                updateState(EATING, actualTime);
                return Simulator.FINISH_DELIVERING_ORDER;
            case EATING:
                updateState(FREE, actualTime);
                return Simulator.FINISH_LUNCH;
            default:
                return -1;
        }
    }
    
    private void updateState(int newState, double actualTime) {
        state = newState;

        switch (state) {
            case FREE:
                /*
                 * Si el nuevo estado es 'Libre', guarda el momento en el q el
                 * cliente anterior terminò de comer, y reinicia todas las
                 * propiedades
                 */
                client.setEndService(actualTime);
                client = null;
                menu = new String[]{"--", "--", "--", "--"};
                menuRnds = new String[]{"--", "--", "--", "--"};
                rndLunch = "--";
                nextEndLunch = nextFinish = Double.MAX_VALUE;
                break;
            case TAKING_ORDER:
                /*
                 * Si el estado nuevo es 'tomando una orden',
                 * revisa si el cliente estaba esperando, y le actualiza
                 * el tiempo de espera. Guarda el tiempo en q fue atendido,
                 * calcula las ordenes de cada cliente, y calcula el prox.
                 * tiempo a terminar.
                 */
                if (client.getStartWaiting() > 0) {
                    client.setEndWaiting(actualTime);
                }
                client.setStartService(actualTime);
                simulateMenuSelection();
                //Un minuto por cada cliente en la mesa
                double takingOrderTime = Configuration.getTimeOrder() * client.getClientsQuantiy();
                //Actualiza el tiempo del proximo fin de evento.
                nextFinish = actualTime + takingOrderTime;
                break;
            case COOKING:
                /*
                 * Si el estado nuevo es 'Cocinando Orden'.
                 * Borra los rnd de menuRnds, se calcula el tiempo
                 * necesario para cocinar todos los pedidos
                 * y se actualiza el nextFinish.
                 */
                menuRnds = new String[]{"--", "--", "--", "--"};
                nextFinish = actualTime + calculateCookingTime();
                break;
            case DELIVERING:
                /*
                 * Si el estado nuevo es 'Entregando Orden'
                 * actualiza el nextFinish
                 */
                nextFinish = actualTime + Configuration.getTimeDeliver();
                break;
            case EATING:
                /*
                 * Si el estado nuevo es 'Consumiendo Orden'
                 * se calcula el tiempo q demoraran los clientes
                 * en consumir la comida, y se calcula el nuevo nextFinish
                 */
                double rnd = Math.random();
                nextEndLunch = simulateLunch(rnd, Math.random());
                nextFinish = actualTime + getNextEndLunch();
                break;
            default:
                break;
        }
    }

    private void simulateMenuSelection() {
        for (int i = 0; i < client.getClientsQuantiy(); i++) {
            double rnd = Math.random();
            menuRnds[i] = String.valueOf(rnd);
            menu[i] = getMenuSelected(rnd);
        }
    }

     /**
     * @return the tableID
     */
    public int getTableID() {
        return tableID;
    }

    /**
     * @param tableID the tableID to set
     */
    public void setTableID(int tableID) {
        this.tableID = tableID;
    }

    /**
     * @return the menu
     */
    public String[] getMenu() {
        return menu;
    }

    /**
     * @param menu the menu to set
     */
    public void setMenu(String[] menu) {
        this.menu = menu;
    }

    /**
     * @return the client
     */
    public Client getClient() {
        return client;
    }

    /**
     * @param client the client to set
     */
    public void setClient(Client client) {
        this.client = client;
        for (String string : getMenu()) {
            string = "-";
        }
    }

    /**
     * Simulate the orders that each client will take,
     * and returns an array with the randoms numbers generated
     * for each client.
     */
    public double[] simulateOrders() {
        double[] rnds = new double[client.getClientsQuantiy()];
        for (int i = 0; i < rnds.length; i++) {
            //Genera un numero aleatoreo para cada cliente
            rnds[i] = Math.random();
            menu[i] = getMenuSelected(rnds[i]);
        }
        return rnds;
    }

    /**
     * Selects wich menu corresponds with the random number given.
     * @param rnd Random Number
     * @return String name of the menu selected
     */
    public String getMenuSelected(double rnd) {
        String menu = "";
        if (rnd < Configuration.getPM1()) {
            menu = "Menu 1";
        } else {
            menu = "Menu 2";
        }
        return menu;
    }

    /**
     * @return the nextFinish
     */
    public double getNextFinish() {
        return nextFinish;
    }

    /**
     * @return the nextFinish
     */
    public String getPrittyNextFinish() {
        if (nextFinish > 0) {
            return String.valueOf(nextFinish);
        } else {
            return "--";
        }
    }

    /**
     * @return the menuRnds
     */
    public String[] getMenuRnds() {
        return menuRnds;
    }

    /**
     * Quantity of Menu 1 x Time to Cook Menu 1
     * + Quantity of Menu 2 x Time to Cook Menu 2
     * @return Total time needed to cook the orders
     */
    private double calculateCookingTime() {
        int countM1, countM2;
        countM1 = countM2 = 0;
        for(int i = 0; i < client.getClientsQuantiy(); i++)
        {
            if(menu[i].compareToIgnoreCase("Menu 1")==0)
                countM1++;
            else
                countM2++;
        }
        return countM1*Configuration.getTM1() + countM2*Configuration.getTM2();
    }

    private double simulateLunch(double rnd1, double rnd2) {
        Generator gen = new Generator();
        return gen.generateNormalDistribution(
                60*Configuration.getLunchMedia(), 
                Configuration.getLunchVariance(),
                rnd1, rnd2);        
    }

    /**
     * @return the nextEndLunch
     */
    public double getNextEndLunch() {
        return nextEndLunch;
    }
    
    /**
     * @return the nextEndLunch
     */
    public String getPrettyNextEndLunch() {
        if (nextEndLunch > 0) {
            return String.valueOf(nextEndLunch);
        } else {
            return "--";
        }
    }

    /**
     * @return the rndLunch
     */
    public String getRndLunch() {
        return rndLunch;
    }
    
    /**
     * Adds the deliver time to the nextFinish time
     */
    public void addDeliveringWatingTime()
    {
        nextFinish += Configuration.getTimeDeliver();
    }
}
